home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / jaq / dist / option.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-01  |  11.4 KB  |  461 lines

  1. /*
  2.  * option.c --
  3.  *
  4.  *    Routines to do command line option processing.
  5.  *
  6.  * Copyright 1986, 1991 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  */
  15.  
  16. #ifndef lint
  17. static char rcsid[] = "$Header: /sprite/src/lib/c/etc/RCS/option.c,v 1.9 91/01/28 16:44:36 kupfer Exp $ SPRITE (Berkeley)";
  18. #endif not lint
  19.  
  20. #include "option.h"
  21. #include "cfuncproto.h"
  22. #include <stdio.h>
  23. #ifdef HASSTDLIBH
  24. #include <stdlib.h>
  25. #else
  26. extern double strtod();
  27. extern char *malloc();
  28. extern int free();
  29. extern int exit();
  30. #endif
  31. #ifdef SYSV
  32. #include <strings.h>
  33. #else
  34. #include <string.h>
  35. #endif
  36. #include <time.h>
  37. #include <sys/types.h>
  38.  
  39. #define OptNoArg(progName, opt) fprintf(stderr, \
  40.               "Warning: %s option \"-%s\" needs an argument\n", \
  41.               (progName), (opt))
  42.  
  43. /* Forward references: */
  44.  
  45. static void ParseTime _ARGS_ ((char *progName, char *str,
  46.                  time_t *resultPtr));
  47.  
  48.  
  49. /*
  50.  *----------------------------------------------------------------------
  51.  *
  52.  * Opt_Parse --
  53.  *
  54.  *    Process a command line according to a template of accepted
  55.  *    options.  See the manual page and header file for more details.
  56.  *
  57.  * Results:
  58.  *    The number of options that weren't processed by this procedure
  59.  *    is returned, and argv points to an array of unprocessed
  60.  *    options.  (This is all of the options that didn't start with
  61.  *    "-", except for those used as arguments to the options
  62.  *    processed here; it's also anything after an OPT_REST option.)
  63.  *
  64.  * Side effects:
  65.  *    The variables referenced from the option array get modified
  66.  *    if their option was present on the command line.  Can clobber 
  67.  *    the global buffer used by localtime(3).
  68.  *
  69.  *----------------------------------------------------------------------
  70.  */
  71.  
  72. int
  73. Opt_Parse(argc, argv, optionArray, numOptions, flags)
  74.     register int  argc;         /* Number of arguments in argv. */
  75.     char          **argv;           /* Array of arguments */
  76.     Option        optionArray[];    /* Array of option descriptions */
  77.     int              numOptions;        /* Size of optionArray */
  78.     int          flags;        /* Or'ed combination of various flag bits:
  79.                      * see option.h for definitions. */
  80. {
  81.     register Option     *optionPtr; /* pointer to the current option in the
  82.                      * array of option specifications */
  83.     register char     *curOpt;    /* Current flag argument */
  84.     register char     **curArg;   /* Current argument */
  85.     register int      argIndex;   /* Index into argv to which next unused
  86.                      * argument should be copied */
  87.     int           stop=0;        /* Set non-zero to stop processing
  88.                      * arguments when an OPT_REST flag is
  89.                      * encountered */
  90.     int            length;        /* Number of characters in current
  91.                      * option. */
  92.  
  93.     argIndex = 1;
  94.     argc -= 1;
  95.     curArg = &argv[1];
  96.  
  97.     while (argc && !stop) {
  98.     if (**curArg == '-') {
  99.         curOpt = &curArg[0][1];
  100.         curArg += 1;
  101.         argc -= 1;
  102.  
  103.         /*
  104.          * Check for the special options "?" and "help".  If found,
  105.          * print documentation and exit.
  106.          */
  107.  
  108.         if ((strcmp(curOpt, "?") == 0) || (strcmp(curOpt, "help") == 0)) {
  109.         Opt_PrintUsage (argv[0], optionArray, numOptions);
  110.         exit(0);
  111.         }
  112.  
  113.         /*
  114.          * Loop over all the options specified in a single argument
  115.          * (must be 1 unless OPT_ALLOW_CLUSTERING was specified).
  116.          */
  117.  
  118.         while (1) {
  119.         /*
  120.          * Loop over the array of options searching for one with the
  121.          * matching key string.  If found, it is left pointed to by
  122.          * optionPtr.
  123.          */
  124.         for (optionPtr = &optionArray[numOptions - 1];
  125.             optionPtr >= optionArray;
  126.             optionPtr -= 1) {
  127.              if (optionPtr->key == NULL) {
  128.              continue;
  129.              }
  130.              if (*optionPtr->key == *curOpt) {
  131.              if (flags & OPT_ALLOW_CLUSTERING) {
  132.                  length = strlen(optionPtr->key);
  133.                  if (strncmp(optionPtr->key, curOpt, length) == 0) {
  134.                  break;
  135.                  }
  136.              } else {
  137.                  if (strcmp(optionPtr->key, curOpt) == 0) {
  138.                  break;
  139.                  }
  140.              }
  141.              }
  142.         }
  143.  
  144.         if (optionPtr < optionArray) {
  145.             /*
  146.              * No match.  Print error message and skip option.
  147.              */
  148.  
  149.             fprintf(stderr, "Unknown option \"-%s\";", curOpt);
  150.             fprintf(stderr, "  type \"%s -help\" for information\n",
  151.                 argv[0]);
  152.             break;
  153.         }
  154.  
  155.         /*
  156.          * Take the appropriate action based on the option type
  157.          */
  158.  
  159.         if (optionPtr->type >= 0) {
  160.             *((int *) optionPtr->address) = optionPtr->type;
  161.         } else {
  162.             switch (optionPtr->type) {
  163.             case OPT_REST:
  164.                 stop = 1;
  165.                 *((int *) optionPtr->address) = argIndex;
  166.                 break;
  167.             case OPT_STRING:
  168.                 if (argc == 0) {
  169.                 OptNoArg(argv[0], optionPtr->key);
  170.                 } else {
  171.                 *((char **)optionPtr->address) = *curArg;
  172.                 curArg++;
  173.                 argc--;
  174.                 }
  175.                 break;
  176.             case OPT_INT:
  177.                 if (argc == 0) {
  178.                 OptNoArg(argv[0], optionPtr->key);
  179.                 } else {
  180.                 char *endPtr;
  181.  
  182.                 *((int *) optionPtr->address) =
  183.                     strtol(*curArg, &endPtr, 0);
  184.                 if (endPtr == *curArg) {
  185.                     fprintf(stderr,
  186.       "Warning: option \"-%s\" got a non-numeric argument \"%s\".  Setting to 0.\n",
  187.                         optionPtr->key, *curArg);
  188.                 }
  189.                 curArg++;
  190.                 argc--;
  191.                 }
  192.                 break;
  193.             case OPT_TIME:
  194.                 if (argc == 0) {
  195.                 OptNoArg(argv[0], optionPtr->key);
  196.                 } else {
  197.                 ParseTime(argv[0], *curArg, 
  198.                       (time_t *)optionPtr->address);
  199.                 curArg++;
  200.                 argc--;
  201.                 }
  202.                 break;
  203.             case OPT_FLOAT:
  204.                 if (argc == 0) {
  205.                 OptNoArg(argv[0], optionPtr->key);
  206.                 } else {
  207.                 char *endPtr;
  208.  
  209.                 *((double *) optionPtr->address) =
  210.                     strtod(*curArg, &endPtr);
  211.                 if (endPtr == *curArg) {
  212.                     fprintf(stderr,
  213.       "Warning: option \"-%s\" got non-floating-point argument \"%s\".  Setting to 0.\n",
  214.                         optionPtr->key, *curArg);
  215.                 }
  216.                 curArg++;
  217.                 argc--;
  218.                 }
  219.                 break;
  220.             case OPT_GENFUNC: {
  221.                 int        (*handlerProc)();
  222.  
  223.                 handlerProc = (int (*)())optionPtr->address;
  224.  
  225.                 argc = (* handlerProc) (optionPtr->key, argc,
  226.                     curArg);
  227.                 break;
  228.             }
  229.             case OPT_FUNC: {
  230.                 int (*handlerProc)();
  231.  
  232.                 handlerProc = (int (*)())optionPtr->address;
  233.                 
  234.                 if ((* handlerProc) (optionPtr->key, *curArg)) {
  235.                 curArg += 1;
  236.                 argc -= 1;
  237.                 }
  238.                 break;
  239.             }
  240.             case OPT_DOC:
  241.                 Opt_PrintUsage (argv[0], optionArray, numOptions);
  242.                 exit(0);
  243.                 /*NOTREACHED*/
  244.             }
  245.         }
  246.         /*
  247.          * Advance to next option
  248.          */
  249.  
  250.         if (flags & OPT_ALLOW_CLUSTERING) {
  251.             curOpt += length;
  252.             if (*curOpt == 0) {
  253.             break;
  254.             }
  255.         } else {
  256.             break;
  257.         }
  258.         }
  259.     } else {
  260.         /*
  261.          * *curArg is an argument for which we have no use, so copy it
  262.          * down.
  263.          */
  264.         argv[argIndex] = *curArg;
  265.         argIndex += 1;
  266.         curArg += 1;
  267.         argc -= 1;
  268.  
  269.         /*
  270.          * If this wasn't an option, and we're supposed to stop parsing
  271.          * the first time we see something other than "-", quit.
  272.          */
  273.         if (flags & OPT_OPTIONS_FIRST) {
  274.         stop = 1;
  275.         }
  276.     }
  277.     }
  278.  
  279.     /*
  280.      * If we broke out of the loop because of an OPT_REST argument, we want
  281.      * to copy the rest of the arguments down, so we do.
  282.      */
  283.     while (argc) {
  284.     argv[argIndex] = *curArg;
  285.     argIndex += 1;
  286.     curArg += 1;
  287.     argc -= 1;
  288.     }
  289.     argv[argIndex] = (char *)NULL;
  290.     return argIndex;
  291. }
  292.  
  293.  
  294. /*
  295.  *----------------------------------------------------------------------
  296.  *
  297.  * Opt_PrintUsage --
  298.  *
  299.  *    Print out a usage message for a command.  This prints out the
  300.  *    documentation strings associated with each option.
  301.  *
  302.  * Results:
  303.  *    none.
  304.  *
  305.  * Side effects:
  306.  *    Messages printed onto the console.
  307.  *
  308.  *----------------------------------------------------------------------
  309.  */
  310.  
  311. void
  312. Opt_PrintUsage(commandName, optionArray, numOptions)
  313.     char *commandName;
  314.     Option optionArray[];
  315.     int numOptions;
  316. {
  317.     register int i;
  318.     int width;
  319.  
  320.     /*
  321.      * First, compute the width of the widest option key, so that we
  322.      * can make everything line up.
  323.      */
  324.  
  325.     width = 4;
  326.     for (i=0; i<numOptions; i++) {
  327.     int length;
  328.     if (optionArray[i].key == NULL) {
  329.         continue;
  330.     }
  331.     length = strlen(optionArray[i].key);
  332.     if (length > width) {
  333.         width = length;
  334.     }
  335.     }
  336.  
  337.     fprintf(stderr, "Usage of command \"%s\"\n", commandName);
  338.     for (i=0; i<numOptions; i++) {
  339.     if (optionArray[i].type != OPT_DOC) {
  340.         fprintf(stderr, " -%s%-*s %s\n", optionArray[i].key,
  341.             width+1-strlen(optionArray[i].key), ":",
  342.             optionArray[i].docMsg);
  343.         switch (optionArray[i].type) {
  344.         case OPT_INT: {
  345.             fprintf(stderr, "\t\tDefault value: %d\n",
  346.                 *((int *) optionArray[i].address));
  347.             break;
  348.         }
  349.         case OPT_FLOAT: {
  350.             fprintf(stderr, "\t\tDefault value: %lg\n",
  351.                 *((double *) optionArray[i].address));
  352.             break;
  353.         }
  354.         case OPT_STRING: {
  355.             if (*(char **)optionArray[i].address != (char *) NULL) {
  356.                 fprintf(stderr, "\t\tDefault value: \"%s\"\n",
  357.                     *(char **) optionArray[i].address);
  358.                 break;
  359.             }
  360.         }
  361.         default: {
  362.             break;
  363.         }
  364.         }
  365.     } else {
  366.         fprintf(stderr, " %s\n", optionArray[i].docMsg);
  367.     }
  368.     }
  369.     fprintf(stderr, " -help%-*s Print this message\n", width-3, ":");
  370. }
  371.  
  372.  
  373. /*
  374.  *----------------------------------------------------------------------
  375.  *
  376.  * ParseTime --
  377.  *
  378.  *    Convert a date and time from some string representation to 
  379.  *    something we can compute with.
  380.  *
  381.  * Results:
  382.  *    If str points to a parsable time, the corresponding UNIX time 
  383.  *    value (seconds past the epoch) is returned through resultPtr.
  384.  *
  385.  * Side effects:
  386.  *    Can clobber the global buffer used by localtime(3).
  387.  *
  388.  *----------------------------------------------------------------------
  389.  */
  390.  
  391. static void
  392. ParseTime(progName, str, resultPtr)
  393.     char    *progName;    /* name that the program was called as */
  394.     char    *str;        /* the string to parse */
  395.     time_t    *resultPtr;    /* pointer to result time value */
  396. {
  397.     long result;        /* the answer */
  398.     char *endPtr;        /* pointer into str, for parsing */
  399.     struct tm pieces;        /* year, month, etc. as integers */
  400.  
  401.     /* 
  402.      * We currently accept the following formats:
  403.      * 
  404.      * (1) an integer number of seconds past the epoch.
  405.      * (2) a string of the form "yy.mm.dd.hh.mm.ss"
  406.      */
  407.     
  408.     result = strtol(str, &endPtr, 0);
  409.     if (endPtr == str) {
  410.     goto parseError;
  411.     }
  412.     if (*endPtr == '\0') {
  413.     *resultPtr = result;
  414.     return;
  415.     }
  416.  
  417.     /* 
  418.      * Not a simple integer, so try form 2. 
  419.      */
  420.     if (*endPtr != '.') {
  421.     goto parseError;
  422.     }
  423.     pieces.tm_year = result;
  424.     if (pieces.tm_year > 1900) {
  425.     pieces.tm_year -= 1900;
  426.     }
  427.     pieces.tm_mon = strtol(endPtr+1, &endPtr, 0) - 1;
  428.     if (endPtr == str || *endPtr != '.') {
  429.     goto parseError;
  430.     }
  431.     pieces.tm_mday = strtol(endPtr+1, &endPtr, 0);
  432.     if (endPtr == str || *endPtr != '.') {
  433.     goto parseError;
  434.     }
  435.     pieces.tm_hour = strtol(endPtr+1, &endPtr, 0);
  436.     if (endPtr == str || *endPtr != '.') {
  437.     goto parseError;
  438.     }
  439.     pieces.tm_min = strtol(endPtr+1, &endPtr, 0);
  440.     if (endPtr == str || *endPtr != '.') {
  441.     goto parseError;
  442.     }
  443.     pieces.tm_sec = strtol(endPtr+1, &endPtr, 0);
  444.     if (endPtr == str || *endPtr != '\0') {
  445.     goto parseError;
  446.     }
  447.  
  448.     result = mktime(&pieces);
  449.     if (result == -1) {
  450.     fprintf(stderr, "%s: can't represent the time \"%s\".\n",
  451.         progName, str);
  452.     } else {
  453.     *resultPtr = result;
  454.     }
  455.     return;
  456.  
  457.  parseError:
  458.     fprintf(stderr, "%s: can't parse \"%s\" as a time.\n", progName, str);
  459.     return;
  460. }
  461.